#include <memory.h>

#include "lib.h"
#include "msggen.h"
#include "msgoutlt.h"
#include "msgcompl.h"
#include "msgplug.h"

MsgGenerator::MsgGenerator(
#ifdef USE_MSGGENCACHE	// not thread-safe
	int32 maxMsgs, 
#endif
	MsgOutlets* outlets)
{
#ifdef USE_MSGGENCACHE	// not thread-safe
	numCompiled = maxMsgs;

	compiled = new MsgCompiled*[numCompiled];
	if (compiled != (MsgCompiled**)0)
		memset(compiled, 0, sizeof(MsgCompiled*) * numCompiled);
	else
		numCompiled = 0;
#endif

	this->outlets = outlets;
}

MsgGenerator::~MsgGenerator()
{
#ifdef USE_MSGGENCACHE	// not thread-safe
	int32 i;

	if (compiled)
	{
		for (i = 0; i < numCompiled; i++)
			delete compiled[i];

		delete [] compiled;
		compiled = (MsgCompiled**)0;
	}
#endif

	delete outlets;
	outlets = (MsgOutlets*)0;
}

void
MsgGenerator::genMsg(
#ifdef USE_MSGGENCACHE	// not thread-safe
	int32 srcId, 
#endif
	char* src, int32 srclen,
#ifdef USE_MSGGENCACHE	// not thread-safe
	uint8 srcMD4[16],
#endif
	MsgPlugs* plugs,
	char* dest, int32* destLen, int32 destMax)
{
	MsgCompiled* msgc;

	dest[0] = '\0';
	*destLen = 0;

	if (outlets && plugs)
	{
#ifdef USE_MSGGENCACHE	// not thread-safe
		msgc = compile(srcId, src, srclen, srcMD4);
#else
		msgc = compile(src, srclen);
#endif
		if (msgc)
		{
			plugEmIn(msgc, src, srclen, plugs, dest, destLen, destMax);

#ifndef USE_MSGGENCACHE	// not thread-safe
			delete msgc;
#endif
		}
	}
}

void
MsgGenerator::genMsgAlloc(
#ifdef USE_MSGGENCACHE	// not thread-safe
	int32 srcId,
#endif
	char* src, int32 srclen,
#ifdef USE_MSGGENCACHE	// not thread-safe
	uint8 srcMD4[16],
#endif
	MsgPlugs* plugs,
	char** dest, int32* destLen)
{
	MsgCompiled* msgc;

	*dest = (char*)0;
	*destLen = 0;

	if (outlets && plugs)
	{
#ifdef USE_MSGGENCACHE	// not thread-safe
		msgc = compile(srcId, src, srclen, srcMD4);
#else
		msgc = compile(src, srclen);
#endif
		if (msgc)
		{
			*destLen = msgc->getAllocLen(srclen, outlets, plugs);
			*dest = new char[*destLen];

			if (*dest)
				plugEmIn(msgc, src, srclen, plugs, *dest, destLen, *destLen);
			else
				*destLen = 0;

#ifndef USE_MSGGENCACHE	// not thread-safe
			delete msgc;
#endif
		}
	}
}

MsgCompiled*
MsgGenerator::compile(
#ifdef USE_MSGGENCACHE	// not thread-safe
	int32 srcid, 
#endif
	char* src, int32 srclen
#ifdef USE_MSGGENCACHE	// not thread-safe
	, uint8 srcMD4[16]
#endif
	)
{
	MsgCompiled* result = (MsgCompiled*)0;

	if (outlets && src
#ifdef USE_MSGGENCACHE	// not thread-safe
		&& compiled && && srcid >= 0 && srcid < numCompiled
#endif
		)
	{
#ifdef USE_MSGGENCACHE	// not thread-safe
#ifdef USE_MSGGENUSEMD4
		result = compiled[srcid];

		if (result && 0 == memcmp(result->getMD4(), srcMD4, 16))
			return result;
#endif

		delete compiled[srcid];

		compiled[srcid] = 
#endif
		result = new MsgCompiled(outlets, src, srclen
#ifdef USE_MSGGENCACHE	// not thread-safe
			, srcMD4
#endif
			);
	}
	return result;
}

void
MsgGenerator::plugEmIn(MsgCompiled* msgc,
	char* src, int32 srclen,
	MsgPlugs* plugs,
	char* dest, int32* destLen, int32 destMax)
{
	MsgOutletPos* op;
	int32 oindex, ofs, id, toCopy, remain;
	char* target;

	target = dest;
	remain = destMax - 1;	// leave room for '\0';

	ofs = 0;
	oindex = -1;
	op = msgc->getFirstOutletPos();
	while (op)
	{
		toCopy = op->getOffsetFromBeginning() - ofs;
		if (!copy(&target, &remain, src + ofs, toCopy))
			break;

		oindex = op->getOutletIndex();
		id = outlets->getId(oindex);

		toCopy = plugs->getLen(id);
		if (!copy(&target, &remain, plugs->getText(id), toCopy))
			break;

		ofs = op->getOffsetFromBeginning() + outlets->getLen(oindex);

		op = op->getNextOutletPos();
	}

	toCopy = srclen - ofs;
	copy(&target, &remain, src + ofs, toCopy);

	*target++ = '\0';
	*destLen = (int32)(target - dest);

	ASSERT(*destLen <= destMax);
}

bool8
MsgGenerator::copy(char** dest, int32* remain, char* src, int32 toCopy)
{
	bool8 copiedAll = true;

	if (toCopy > 0)
	{
		if (toCopy <= *remain)
		{
			memcpy(*dest, src, toCopy);
			*dest += toCopy;
			*remain -= toCopy;
		}
		else
		{
			toCopy = *remain;
			if (toCopy > 0)
			{
				memcpy(*dest, src, toCopy);
				*dest += toCopy;
				*remain -= toCopy;
			}
			copiedAll = false;
		}
	}
	else
	{
		ASSERT(toCopy == 0);	// should NEVER be < 0!
	}
	return copiedAll;
}
